<?php
/**
 * Note:     [Description]
 * Author:   longhui.huang <1592328848@qq.com>
 * DateTime: 2024/3/7 10:37
 */
declare(strict_types=1);

namespace App\Service\Auth;

use App\Exception\Api\JsonWebToken\ExpiredJwtException;
use App\Exception\Api\JsonWebToken\InCorrectJwtException;
use App\Service\BaseService;
use App\Utility\IdUtil;
use App\Utility\Log\Log;
use App\Utility\Redis\RedisUtil;
use EasySwoole\Jwt\Jwt;
use EasySwooleApi\Core\Request\Request;

class TokenService extends BaseService
{
    /**
     * 创建Token 设置永不过期，
     * Token 的时间有效性转到Redis 维护
     *
     * @param string $username
     *
     * @return string
     */
    public function createToken(string $username): string
    {
        $jwtConfig = config('jwt');
        // 加入ID确保生成的 Token 都不一致
        $jti       = IdUtil::simpleUUID();
        $jwtObject = Jwt::getInstance()->setSecretKey($jwtConfig['base64-secret'])->publish();
        $jwtObject->setAlg($jwtConfig['alg']);
        $jwtObject->setAud($jwtConfig['iss']);                                          // 用户
        $jwtObject->setExp(time() + $jwtConfig['token-validity-in-seconds']);           // 过期时间
        $jwtObject->setIat(time());                                                     // 发布时间
        $jwtObject->setIss($jwtConfig['iss']);                                          // 发行人
        $jwtObject->setJti($jti);                                                       // jwt id 用于标识该jwt
        $jwtObject->setSub($username);                                                  // 主题

        // 自定义数据
        $jwtObject->setData(['user' => $username]);

        // 最终生成的token
        return $jwtObject->__toString();
    }

    /**
     * @param string $token
     *
     * @return \EasySwoole\Jwt\JwtObject
     */
    public function parseToken(string $token)
    {
        $jwtConfig = config('jwt');
        $jwtObject = Jwt::getInstance()->setSecretKey($jwtConfig['base64-secret'])->decode($token);
        $status    = $jwtObject->getStatus();
        switch ($status) {
            case  1:
                return $jwtObject;
            case  -1:
                throw new InCorrectJwtException("token无效");
            case  -2:
                throw new ExpiredJwtException("token已过期");
        }
    }

    /**
     * @param string $token 需要检查的token
     */
    public function checkRenewal(string $token): void
    {
        $jwtConfig = config('jwt');
        $onlineKey = $jwtConfig['online-key'];
        $key       = $onlineKey . $token;
        $time      = RedisUtil::ttl($key) * 1000;
        $differ    = $time - microtime(true) * 1000;
        if ($differ <= $jwtConfig['detect']) {
            $renew       = $time + $jwtConfig['renew'];
            $renewSecond = intval($renew / 1000);
            RedisUtil::expire($key, $renewSecond);
        }
    }

    public function getToken(Request $request): ?string
    {
        $jwtConfig     = config('jwt');
        $authorization = $request->header($jwtConfig['header']);

        if ($authorization && strpos($authorization, $jwtConfig['token-start-with']) !== false) {
            return substr($authorization, 7);
        }

        return null;
    }

    /**
     * 获取登录用户RedisKey
     *
     * @param string $token
     *
     * @return string
     */
    public function loginKey(string $jwtSubject, string $token): string
    {
        $md5Token  = md5($token);
        $onlineKey = config('jwt.online-key');
        return $onlineKey . $jwtSubject . '-' . $md5Token;
    }

    /**
     * 初步检测Token
     *
     * @return array|string|string[]|null
     */
    public function resolveToken(Request $request)
    {
        $jwtConfig   = config('jwt');
        $bearerToken = $request->header($jwtConfig['header']);
        if ($bearerToken && strlen($bearerToken) > 0 && strpos($bearerToken, $jwtConfig['token-start-with']) !== false) {
            // 去掉令牌前缀
            return ltrim(str_replace($jwtConfig['token-start-with'], '', $bearerToken));
        } else {
            Log::debug("非法Token: {$bearerToken}");
        }

        return null;
    }
}
